// (c) Copyright 2013-2014 Stephen Gold <sgold@sonic.net>
// Distributed under the terms of the GNU General Public License

/*
 This file is part of the Bats Game.

 The Bats Game is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by the
 Free Software Foundation, either version 3 of the License, or (at your
 option) any later version.

 The Bats Game is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.

 You should have received a copy of the GNU General Public License
 along with the Bats Game.  If not, see <http://www.gnu.org/licenses/>.
 */
package bats.control;

import bats.Assets;
import bats.SpatialProperties;
import bats.indicator.PickIndicator;
import bats.scene.IndicatorsNode;
import bats.scene.PickablesNode;
import bats.scene.Props;
import bats.scene.SceneNode;
import bats.scene.WorldNode;
import bats.unit.UnitClass;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import java.util.logging.Logger;
import jme3utilities.SimpleControl;
import jme3utilities.math.MyVector3f;
import jme3utilities.math.VectorXZ;

/**
 * A simple control to track whether a pickable is picked or not. When this
 * control is enabled, the pickable is picked and its pick indicator becomes
 * visible.
 * <p>
 * The pick indicator is horizontal, and its gnomon indicates the pickable
 * spatial's orientation -- for a unit, its pelvic orientation.
 * <p>
 * Each instance is disabled at creation.
 *
 * @author Stephen Gold <sgold@sonic.net>
 */
public class PickedControl
        extends SimpleControl {
    // *************************************************************************
    // constants

    /**
     * message logger for this class
     */
    final private static Logger logger =
            Logger.getLogger(PickedControl.class.getName());
    /**
     * type of indicator
     */
    final private static String indicatorType = "pick";
    // *************************************************************************
    // fields
    /**
     * the indicator geometry
     */
    private Geometry indicator = null;
    // *************************************************************************
    // constructors

    /**
     * Instantiate a disabled control.
     */
    public PickedControl() {
        super.setEnabled(false);
    }
    // *************************************************************************
    // new methods exposed

    /**
     * Get the world location of the pick indicator.
     *
     * @return a new vector
     */
    public Vector3f getIndicatorLocation() {
        PickablesNode pickables = WorldNode.find(spatial).getPickables();
        assert spatial.getParent() == pickables.getSpatial();
        Vector3f location = pickables.getIndicatorLocation(spatial);
        return location;
    }

    /**
     * Get the world orientation of the pick indicator.
     *
     * @return a new quaternion
     */
    public Quaternion getIndicatorOrientation() {
        VectorXZ mapDirection;
        UnitClass units = WorldNode.find(spatial).getUnitClass(spatial);
        if (units == null) {
            Quaternion rotation = spatial.getLocalRotation();
            Vector3f direction = rotation.mult(Vector3f.UNIT_X);
            mapDirection = MyVector3f.horizontalDirection(direction);
        } else {
            mapDirection = units.getMapPelvicDirection(spatial);
        }
        Vector3f direction = mapDirection.toVector3f();
        Vector3f up = Vector3f.UNIT_Y;
        Quaternion orientation = new Quaternion();
        orientation.lookAt(direction, up);
        return orientation;
    }
    // *************************************************************************
    // AbstractControl methods

    /**
     * Alter this control's enabled status.
     *
     * @param newState true to enable (reveal indicator), false to disable (hide
     * indicator)
     */
    @Override
    public void setEnabled(boolean newState) {
        assert spatial != null;
        createIndicator();
        assert indicator != null;
        IndicatorsNode indicators = SceneNode.find(spatial).getIndicators();
        if (isEnabled() && !newState) {
            indicators.detach(indicator);
        } else if (!isEnabled() && newState) {
            indicators.reveal(indicator);
        }
        super.setEnabled(newState);
    }
    // *************************************************************************
    // private methods

    /**
     * Create the indicator for the pickable.
     */
    private void createIndicator() {
        if (indicator != null) {
            /*
             * The indicator has already been created.
             */
            return;
        }
        assert spatial != null;

        indicator = Assets.loadIndicatorModel(indicatorType);
        String name = String.format("%s %s indicator",
                spatial.getName(), indicatorType);
        indicator.setName(name);

        PickIndicator indicatorControl = new PickIndicator(this);
        indicator.addControl(indicatorControl);

        SceneNode scene = SceneNode.find(spatial);
        float scale = 0.15f / scene.getScaleFactor();
        UnitClass units = scene.getWorld().getUnitClass(spatial);
        if (units != null) {
            scale *= units.getRelativeSize(spatial);
        } else if (SpatialProperties.isProp(spatial)) {
            Props props = scene.getWorld().getProps();
            scale *= props.getRelativeSize(spatial);
        }
        indicator.setLocalScale(scale);

        if (isEnabled()) {
            scene.getIndicators().reveal(indicator);
        }
    }
}